/**
 * 
 */
package menus.zone;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.geom.Point2D;
import java.lang.reflect.Field;
import java.util.Vector;

import components.SetupExperiment;
import components.intertitles.BlockInstructions;
import components.intertitles.Trial;
import components.intertitles.TrialInstructions;

import fr.inria.insitu.noe.Platform;
import fr.lri.swingstates.canvas.CEllipse;
import fr.lri.swingstates.canvas.CSegment;
import fr.lri.swingstates.canvas.CShape;
import fr.lri.swingstates.canvas.CStateMachine;
import fr.lri.swingstates.canvas.Canvas;
import fr.lri.swingstates.gestures.Gesture;
import fr.lri.swingstates.sm.StateNotFoundException;
import menus.common.AbstractItem;
import menus.common.AbstractMenu;
import menus.common.AdHocGestureAnalyzer;
import menus.common.TapAndStrokeMenu;

/**
 * @author insitu
 *
 */
public class ZoneMenu extends TapAndStrokeMenu {

	/*
	 * DONE :  separators
	 */
	
	protected double menuRadius = 150;
	protected double itemLength = 100;
	
	/**
	 * Used for the experiment
	 */
	protected double tapDrawAngle;
	
	/**
	 * Used for the experiment
	 */
	protected double strokeAngle;
	
	protected ZoneMenuStateMachine stateMachine;
	
	
	
	/**
	 * @param canvas
	 * @param itemLabels
	 */
	public ZoneMenu(Canvas canvas, String[][] itemLabels) {
		
		super(canvas, itemLabels);
		this.stateMachine = new ZoneMenuStateMachine(canvas, this);
		
		analyzer = new AdHocGestureAnalyzer();
		
	}
	
	public ZoneMenu(Canvas canvas, String[][] itemLabels, double menuRadius, double itemLength) {
		
		this(canvas, itemLabels);
		
		this.menuRadius = menuRadius;
		this.itemLength = itemLength;
		
	}

	@Override
	protected AbstractItem createItem(String[][] itemLabels, int i, int j, String tag) {
		
		double zoneAngle = i * 2 * Math.PI / itemLabels.length;
		double itemAngle = j * 2 * Math.PI / itemLabels[i].length;
		
		return new ZoneMenuItem(this, itemLabels[i][j], zoneAngle, itemAngle, tag);
		
	}

	@Override
	public AbstractItem getItem(Point2D tap, Gesture draw) {
		
		Vector<Point2D> gesturePoints = ((AdHocGestureAnalyzer)analyzer).adHocAnalyse(draw);
		
		if (gesturePoints.size() != 2){
			
			shapeError = true;
			
			return null;
		}
		
		shapeError = false;
		
		tapDrawAngle = Math.atan2(
				tap.getY() - gesturePoints.get(0).getY(), 
				gesturePoints.get(0).getX() - tap.getX()
		);
		
		strokeAngle = Math.atan2(
				gesturePoints.get(0).getY() - gesturePoints.get(1).getY(), 
				gesturePoints.get(1).getX() - gesturePoints.get(0).getX()
		);
		
		if (tapDrawAngle < 0)
			tapDrawAngle += 2 * Math.PI;
		
		if (strokeAngle < 0)
			strokeAngle += 2 * Math.PI;
		
		int selectedZone = normalize(tapDrawAngle, 2 * Math.PI / items.length) % items.length;
		int j = normalize(strokeAngle, 2 * Math.PI / items[selectedZone].length) % items[selectedZone].length;
		
		System.out.println("\n\t\tSELECTED : " + (selectedZone * items.length + j) + "\n");
		
		return items[selectedZone][j];
		
	}
	
	protected int normalize(double value, double unit){
		return new Double(Math.floor( value / unit + .5 )).intValue();
	}
	
	public double getMenuRadius() {
		return menuRadius;
	}

	public double getItemLength() {
		return itemLength;
	}

	
	// ----------------------------------------------------------------------------------
	
	
	@Override
	public boolean isSelectionEnded() {
		return stateMachine.isSelectionEnded();
	}

	@Override
	protected void setLengthError() {
		
		lengthError = 0;
		
	}

	@Override
	protected void setOrientationError() {
		
		orientationError = strokeAngle - (targetIndex % items[0].length) * 2 * Math.PI / items[0].length;
		
		if (orientationError < -Math.PI)
			orientationError += 2 * Math.PI;
		else if (orientationError > Math.PI)
			orientationError -= 2 * Math.PI;
		
	}

	@Override
	protected void setShapeError() {
		
		// Already done
		
	}
	
	@Override
	protected void setPositionError() {
		
		positionError = tapDrawAngle - (targetIndex / items[0].length) * 2 * Math.PI / items.length;
		
		if (positionError < -Math.PI)
			positionError += 2 * Math.PI;
		else if (positionError > Math.PI)
			positionError -= 2 * Math.PI;
		
	}

	@Override
	public void resetSelection() {
		stateMachine.resetSelection();
	}

	@Override
	protected void setSelectionDuration() {
		selectionDuration = stateMachine.getSelectionTimes()[1];
	}

	@Override
	protected void setStartTime() {
		startTime = stateMachine.getSelectionTimes()[0];
	}
	
	@Override
	public ZoneMenuStateMachine getStateMachine() {
		return stateMachine;
	}
	
	@Override
	public void drawInstructions(Canvas canvas, CShape instructionZone, String... tags) {
		
		int zone = targetIndex / items[0].length;
		int item = targetIndex % items[0].length;
		
		// Zone separators
		
		for (int z = 0; z < items.length; z++){
			canvas.newSegment(0, 0, 150, 0)
				.setStroke(new BasicStroke(2))
				.setOutlinePaint(new Color(160, 160, 160))
				.setFilled(false)
				
				.setReferencePoint(0 ,0)
				.translateTo(instructionZone.getCenterX(), instructionZone.getCenterY())
				.rotateBy((z+.5)*2*Math.PI/items.length)
				
				.addTag(TrialInstructions.InstructionTag)
				
				.setPickable(false)
				.setDrawable(true);
		}
		
		// Center
		
		super.drawInstructions(canvas, instructionZone, 20, tags);
		
		// Arrow
		
		CShape arrow = ((ZoneMenuItem)items[zone][item]).createShape(60, 60);
		arrow.translateBy(instructionZone.getCenterX(), instructionZone.getCenterY())
			
			.setDrawable(true);
		
		canvas.addShape(arrow);
		arrow.addTag(TrialInstructions.InstructionTag);
		
	}
	
	public static void drawMenu(){
		
		Canvas canvas = ((SetupExperiment)Platform.getExperiment().getSetUp()).getGestureArea();
		int capacity = (Integer)Platform.getInstance().getFactorValue(Trial.Capacity);
		int nbItemsPerZone = (capacity == 16 ? 4 : 8);
		int nbZones = capacity / nbItemsPerZone;
		
		double zoneAngle, itemAngle;
		
		for (int zIndex = 0; zIndex < nbZones; zIndex++){
			
			zoneAngle = zIndex * 2 * Math.PI / nbZones;
			
			CSegment separator = new CSegment(0, 0, 400, 0);
			separator.setFilled(false)
					.setOutlinePaint(Color.darkGray)
					.setStroke(new BasicStroke(1, 0, 0, 1, new float[]{5, 5}, 0))
			
					.setDrawable(true)
					.setPickable(false)
					
					.setReferencePoint(0, 0)
					.rotateBy(-zoneAngle + .5 * 2 * Math.PI / nbZones);
			
			canvas.addShape(separator);
			separator.addTag(BlockInstructions.InstructionTag);
			separator.setDrawable(true);
			separator.translateBy(canvas.getWidth()/2, canvas.getHeight()/2);
			
			for (int iIndex = 0; iIndex < nbItemsPerZone; iIndex++){
				
				itemAngle = iIndex * 2 * Math.PI / nbItemsPerZone;
				
				CShape mark = ZoneMenuItem.createShape(zoneAngle, itemAngle, 200, 200, "");
				
				canvas.addShape(mark);
				mark.addTag(BlockInstructions.InstructionTag);
				mark.setDrawable(true);
				mark.translateBy(canvas.getWidth()/2, canvas.getHeight()/2);
			}
		}
		
	}

}